package fr.utt.lo02.CrazyEights.view;


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.Observable;
import java.util.Observer;

import fr.utt.lo02.CrazyEights.model.Game;
import fr.utt.lo02.CrazyEights.model.Game.EndMethod;
import fr.utt.lo02.CrazyEights.model.cards.CardRank;
import fr.utt.lo02.CrazyEights.model.cards.CardSuit;
import fr.utt.lo02.CrazyEights.model.players.Human;
import fr.utt.lo02.CrazyEights.model.players.Player;


/**
 * Represents the console
 * @author Jeremy
 *
 */
public class Console extends Thread implements Observer{
	private InputStream in;
	private PrintStream out;
	private boolean changeSuit;
	
	public Console(){
		Game.getInstance().addObserver(this);
		this.in=System.in;
		this.out=System.out;
		changeSuit = false;
	}
	
	/**
	 * Reads something write by the user
	 * @return String written by user
	 */
	public String readString(){
		String read=null;
		BufferedReader buIn = new BufferedReader(new InputStreamReader(in));
			try {
				read=buIn.readLine();
			} catch (IOException e) {
				e.printStackTrace();
			}
		return read;
	}
	
	/**
	 * Use to set game Settings
	 */
	public void gameSettings(){
		boolean ok;
		int nbPlayers = 2;
		int nbHumanPlayers = 1;
		String string="0";
		out.println("***Game Settings***");
		do{
			out.print("Number of players (between 2 and 8): ");
			string = readString();
			if(!string.matches("[0-9]+")){
				ok = false;
			}else{
				if(Integer.parseInt(string)<2 || Integer.parseInt(string)>8){
					ok = false;
				}else{
					ok = true;
				}
			}
			if(!ok){
				out.println("Incorrect number of players");
			}
			else{
				Game.getInstance().setNbOfPlayers(Integer.parseInt(string));
				nbPlayers = Integer.parseInt(string);
			}
		}while(!ok);
		
		do{
			out.print("Number of human players (between 1 and "+nbPlayers+"): ");
			string = readString();
			if(!string.matches("[0-9]+")){
				ok = false;
			}else{
				if(Integer.parseInt(string)<1 || Integer.parseInt(string)>nbPlayers){
					ok = false;
				}else{
					ok = true;
				}
			}
			if(!ok){
				out.println("Incorrect number of human players");
			}
			else{
				nbHumanPlayers = Integer.parseInt(string);
			}
		}while(!ok);
		
		do{
			out.print("Number of cards by player (between 8 and 15): ");
			string = readString();
			if(!string.matches("[0-9]+")){
				ok = false;
			}else{
				if(Integer.parseInt(string)<8 || Integer.parseInt(string)>15){
					ok = false;
				}else{
					ok = true;
				}
			}
			if(!ok){
				out.println("Incorrect number of cards by player");
			}
			else{
				Game.getInstance().setNbOfCardsByPlayer(Integer.parseInt(string));
			}
		}while(!ok);
		
		do{
			out.print("End Method: \n1. Points\n2. Rounds\nYour Choice (1 or 2):");
			string = readString();
			if(!string.matches("[0-9]+")){
				ok = false;
			}else{
				if(Integer.parseInt(string)<1 || Integer.parseInt(string)>2){
					ok = false;
				}else{
					ok = true;
				}
			}
			if(!ok){
				out.println("Incorrect end method");
			}
			else{
				switch(Integer.parseInt(string)){
				case 1:
					Game.getInstance().setEndMethod(EndMethod.Points);
					break;
				case 2:
					Game.getInstance().setEndMethod(EndMethod.Rounds);
					break;
				default:
					Game.getInstance().setEndMethod(EndMethod.Points);
					break;
				}
			}
		}while(!ok);
		
		Game.getInstance().resetPlayersList();
		for(int i=1;i<=nbHumanPlayers;i++){
			do{
				out.print("Enter name for human player "+i+": ");
				string = readString();
				if(string.matches("[a-zA-Z0-9]+")){
					ok = true;
				}
				else{
					ok = false;
				}
			}while(!ok);
			Game.getInstance().addHumanPlayer(string);
		}
		
		do{
			out.print("Enter the target number of "+Game.getInstance().getEndMethod()+": ");
			string = readString();
			if(string.matches("[0-9]+")){
				ok = true;
			}
			else{
				ok = false;
			}
		}while(!ok);
		Game.getInstance().setTarget(Integer.parseInt(string));
		Game.getInstance().init();
	}
	
	/**
	 * Used when the suit has changed
	 */
	public void changedSuit(){
		this.changeSuit = false;
		if(Game.getInstance().getDiscardPile().getCardOnTop().getRank()==CardRank.Joker){
			Game.getInstance().getPlayersList().skipNextPlayer();
		}
		else{
			Game.getInstance().getPlayersList().nextPlayer();
		}
		repaintGame();
	}
	
	/**
	 * The thread used by console to play
	 */
	public void run(){
		out.println("***Crazy Eights***");
		String string="";
		out.print("Action: ");
		while(!string.equals("quit")){
			string=readString();
			if(string.equals("new game")){
				gameSettings();
			}else if(string.equals("quit")){
				System.exit(0);
			}else if(Game.getInstance().isInitialized()){
				if(Game.getInstance().isGameEnded()){
					
				}else if(changeSuit){
					if(string.matches("[0-9]+")){
						switch(Integer.parseInt(string)){
						case 1:
							Game.getInstance().getDiscardPile().setGameSuit(CardSuit.CLUBS);
							changedSuit();
							break;
						case 2:
							Game.getInstance().getDiscardPile().setGameSuit(CardSuit.DIAMONDS);
							changedSuit();
							break;
						case 3:
							Game.getInstance().getDiscardPile().setGameSuit(CardSuit.HEARTS);
							changedSuit();
							break;
						case 4:
							Game.getInstance().getDiscardPile().setGameSuit(CardSuit.SPADES);
							changedSuit();
							break;
						default:
							out.println("Incorrect suit\nAction: ");
						}
					}else{
						out.println("Incorrect suit\nAction: ");
					}
				}else if(string.equals("draw")){
					Game.getInstance().getPlayersList().getCurrentPlayer().play(null);
				}else if(string.startsWith("play")){
					Player current = Game.getInstance().getPlayersList().getCurrentPlayer();
					String playedCard = string.substring(5);
					if(playedCard.matches("[0-9]+")){
						if(Integer.parseInt(playedCard)>0 && Integer.parseInt(playedCard)<=current.getNbCardsInHand()){
							if(Game.getInstance().getDiscardPile().isPlayable(current.getCardsInHand().get(Integer.parseInt(playedCard)-1))){
								current.play(current.getCardsInHand().get(Integer.parseInt(playedCard)-1));
							}else{
								out.print("Impossible to play this card\nAction: ");
							}
						}else{
							out.print("Incorrect number\nAction: ");
						}
					}else{
						out.print("Incorrect number\nAction: ");
					}
				}else{
					out.print("Incorrect action\nAction: ");
				}
			}else{
				out.print("Incorrect action\nAction: ");
			}
		}
	}
	
	/**
	 * Call when the game is ended
	 */
	public void endGame(){
		out.println(Game.getInstance().getPlayersList().getRanking().get(0).getName()+" is the winner!");
		int rank =1;
		for(Player p : Game.getInstance().getPlayersList().getRanking()){
			out.println(rank+". "+p.getName()+" ==> "+p.getPoints()+" points");
			rank++;
		}
		out.print("Action: ");
	}
	
	/**
	 * Refresh the display of the game
	 */
	public void repaintGame(){
		Thread repaint = new Thread(){
			public void run(){
				if(Game.getInstance().getPlayersList().getCurrentPlayer() instanceof Human){
					Player current = null;
					for(Player p : Game.getInstance().getPlayersList().getPlayersList()){
						if(p == Game.getInstance().getPlayersList().getCurrentPlayer()){
							current = p;
						}
						else{
							out.println(p.getName()+": "+p.getNbCardsInHand()+" remaining cards");
						}
					}
					out.println(current.getName());
					out.println(current.cardsInHandToString());
					out.println("DiscardPile: "+Game.getInstance().getDiscardPile().toString()+". Current Suit: "+Game.getInstance().getDiscardPile().getGameSuit());
					out.print("Action: ");
				}
			}
		};
		repaint.start();
	}
	
	/**
	 * Shows the change suit text
	 */
	public void changeSuit(){
		out.print("Choose Suit\n1. Club\n2. Diamond\n3. Heart\n4. Spade\nAction: ");
		changeSuit = true;
	}
	
	public void update(Observable o, Object arg){
		if(Game.getInstance().isGameEnded()){
			Thread end = new Thread(){
				public void run(){
					endGame();
				}
			};
			end.start();
		}
		else if(Game.getInstance().getChangeSuit()){
			Thread change = new Thread(){
				public void run(){
					changeSuit();
				}
			};
			change.start();
		}
		else{
			repaintGame();
		}
	}
}
